<?php
/**
 *
 * @author ninetyseven.
 * @date 2023-03-27 22:53
 */

namespace App\Services;

use App\Enum\CodeEnum;
use App\Enum\WorkermanEnum;
use App\Lib\Jwt;
use App\Models\CmzjScheduleModel;
use App\Models\MpEventModel;
use App\Models\OrderModel;
use Exception;
use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;
use Psr\Container\NotFoundExceptionInterface;


class WorkermanService
{
    /**
     * 获取公益出行的购买结果
     * @param $client_id
     * @param $message
     * @return void
     */
    private static $enableLog= true;
    public static function getBuyGycxResult( $client_id, $message)
    {
        return self::_getBuyCardResult($client_id,$message);
    }
    public static function getBuyEnterpriseCommuteResult( $client_id, $message)
    {
        return self::_getBuyCardResult($client_id,$message);
    }


    /**
     * 获取订单使用积分的支付结果
     *
     * @param $client_id
     * @param $message
     * @return array|false|void
     */
    public static function getOrderPayResult( $client_id, $message)
    {
        $userId = self::_getUserId($client_id, $message);
        if( !empty($userId) ){
            $data = $message['data'];
            $referNo = $data['refer_no'] ?? '-';
            $record = OrderModel::query()->where(['user_id'=>$userId, 'order_no'=>$referNo])->first();
            if( !empty($record) ){
                $record = $record->toArray();
                return [
                    'refer_no'  => $record['order_no'],
                    'status'    => $record['pay_status'],
                    'order_id'  => $record['id']
                ];
            }else{
                self::sendError($client_id,  '订单付款失败，账户余额不足。',CodeEnum::PARAM_ERROR, 'getOrderPayResult');
                return false;
            }
        }
    }
    public static function addMpEvent( $client_id, $message)
    {
        try {
            $data = $message['data'];
            if( !isset($data['event']) ) {
                throw new Exception("未传递参数event.");
            }
            $createData['event_name']   = $data['event'];
            $createData['user_id']      = $data['user_id'] ?? '0';
            $createData['user_phone']   = $data['user_phone'] ?? '-';
            $createData['client_data']  = json_encode($data,JSON_UNESCAPED_UNICODE);
            $id = MpEventModel::query()->create($createData);
            return ['id'=>$id];
        }catch (Exception $e){
            self::sendError($client_id,  sprintf('添加事件统计失败（%s）。', $e->getMessage()),CodeEnum::PARAM_ERROR, 'addMpEvent');
            return false;
        }
    }

    /**
     * 返回每个组的在线用户数
     *
     * @param $client_id
     * @param $message
     * @return array|string[]
     * @throws NotFoundExceptionInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     */
    public static function getStatics($client_id, $message): array
    {
        try {
            return self::_getStatics();
        }catch (Exception $e){
            return ['exception'=>$e->getMessage()];
        }
    }

    /**
     * 获取最新的监控数据
     *
     * @param $client_id
     * @param $message
     * @return array
     */
    public static function getLatestMonitor($client_id, $message): array
    {
        try {
            list($monitorId) = self::_getDataValue($message, ['latest_monitor_id']);
//            log::notice('get data monitorId='.$monitorId);
            return MonitorService::getLatestMonitor($monitorId);
        }catch (Exception $e){
            return ['exception'=>$e->getMessage()];
        }
    }

    /**
     * 将client绑定到source组
     * @param $client_id
     * @param $message
     * @return void
     */
    public static function IamIn($client_id, $message)
    {
        //刚刚链接时 使用IAmIn方法
        Gateway::joinGroup($client_id,WorkermanEnum::ROOM[$message['source']]);
        if( !empty($message['token']) )$userId = self::_getUserId( $client_id, $message);
        $userId = $userId ?? 0;
//        self::sendResult($client_id, $message['method'],['user_id'=>$userId]);

        $redis = app('redis.connection');

        $key = sprintf(WorkermanEnum::REDIS_KEY_TOTAL_USERS_BY_GROUP,date('Ymd'),WorkermanEnum::ROOM[$message['source']]);
        if( !$redis->exists($key) ) $redis->setex($key, 86400, 1);
        else $redis->incr($key);

        return $userId;
    }

    /**
     * 添加异常记录
     * @param $client_id
     * @param $message
     * @return array
     */
    public static function addMpException($client_id, $message): array
    {
        $userId = self::_getUserId( $client_id, $message);
        $data = $message['data'] ?? [];
        $reason = $data['reason'] ?? '空';
        MonitorService::addMpException($userId,$reason);
        return ['result'=>'记录成功', 'user_id'=>$userId];
    }
//====================================================================================================================
//                                      以下是常用方法
//====================================================================================================================
    public static function sendResult($client_id,$method, $data=[])
    {

        Gateway::sendToClient($client_id,json_encode(['method'=>$method ,'code'=>CodeEnum::SUCCESS, 'data'=>$data,'msg'=>'SUCCESS']));
        self::_log($client_id, $data);
    }
    public static function sendError($client_id, $message, $code=CodeEnum::ERROR,$method='')
    {
        Gateway::sendToClient($client_id,json_encode(['method'=>$method, 'code'=>$code, 'client_id'=>$client_id,'msg'=>$message,'data'=>[]]));
    }

//====================================================================================================================
//                                      以上是公开方法,以下是私有方法
//====================================================================================================================
    /**
     * 获取user id, 途径
     *  1. gateway
     *  2. token
     * @param $client_id
     * @param $message
     * @return int|mixed
     */
    private static function _getUserId($client_id, $message)
    {
        $userId = Gateway::getUidByClientId($client_id);
        // 未绑定的话，先绑定
        if( empty($userId) && isset($message['token'])){
            $userId = self::_getUidFromTokenAndBindToClientId($client_id, $message['token']);
        }
        return $userId ?? 0;
    }
    private static function _getUidFromTokenAndBindToClientId($client_id, $token)
    {
        $userId = self::decodeTotken($client_id, $token);
        if( !empty($userId) )
        {
            Gateway::bindUid( $client_id, $userId );
        }
        return $userId;
    }
    private static function decodeTotken($client_id, $token)
    {
        $token = trim(str_replace('Bearer', '', $token));
        try {
            //验证token
            $result = self::_decodeJwt($token);
            $userId = $result['user_id'] ?? 0;
        } catch (Exception $e) {
            $userId = 0 ;
            self::sendError($client_id,  'TOKEN 解析失败。',CodeEnum::SIGN_ERROR);
        }
        return $userId;
    }

    private static function sendResultToGroup($group,$data,$method)
    {
        Gateway::sendToGroup($group,json_encode(['method'=>$method ,'code'=>CodeEnum::SUCCESS, 'type'=>'result','data'=>$data,'msg'=>'SUCCESS']));
    }

    /**
     * 累计人数
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private static function _getStatics(): array
    {
        try{
            $redis = app('redis.connection');
            $data = [];
            foreach( WorkermanEnum::ROOM as $room)$data['online_count_'.$room] = Gateway::getClientIdCountByGroup($room);
            foreach( WorkermanEnum::ROOM as $room)$data['accumulated_count_'.$room] = $redis->get(sprintf(WorkermanEnum::REDIS_KEY_TOTAL_USERS_BY_GROUP,date('Ymd'),$room)) ?? 0;
            return $data;
        }catch (Exception $e){
            return ['inner_msg'=>"exec _getStatics error:".$e->getMessage()];
        }
    }
    private static function _log($client_id, $data):void
    {
        if( self::$enableLog ) {
            $uid = Gateway::getUidByClientId($client_id);
            Log::debug('UID=' . $uid . 'DATA========>' . var_export($data, true));
        }
    }
    private static function _getBuyCardResult( $client_id, $message )
    {
        $userId = self::_getUserId($client_id, $message);
        if( !empty($userId) ){
            $data = $message['data'];
            $cardNo = $data['refer_no'] ?? '-';
            $record = CmzjScheduleModel::query()->where(['user_id'=>$userId, 'refer_no'=>$cardNo])->first();
            if( !empty($record) ){
                $record = $record->toArray();
                return [
                    'refer_no'  => $record['refer_no'],
                    'status'    => $record['status'],
                    'msg'       => $record['result']
                ];
            }else{
                self::sendError($client_id,  '无法根据单号取到数据。',CodeEnum::PARAM_ERROR);
            }
        }
        self::sendError($client_id,  '用户未登录。',CodeEnum::PARAM_ERROR);
        return false;
    }

    /**
     * 获取 message中的数据，并验证是否存在
     *
     * @param $message
     * @param $fields
     * @return array
     * @throws Exception
     */
    private static function _getDataValue($message, $fields): array
    {
        try {

            if (empty($message['data'])) {
                Log::error('! isset message.data=>'.var_export($message['data'], true));
                throw new Exception("未传递data参数");
            }
            $params = $message['data'];
            $result = [];
            foreach ($fields as $field) {
                if (!empty($fields[$field])) {
                    Log::error(sprintf('! isset message.data.%s=>%s',$field,var_export($message['data'], true)));
                    throw new Exception("未传递data参数中的" . $field);
                }
                $result[] = $params[$field];
            }
            return $result;
        }catch( Exception $e){
            throw new Exception($e->getMessage());
        }
    }
    /**
     *  解析token并验证是否过期
     *
     * @param $token
     * @return mixed
     * @throws Exception
     */
    private static function _decodeJwt($token)
    {
        $result = Jwt::decodeToken($token);
        // 若过期
        if( time() - $result['iat'] > $result['exp'] ) {
            Log::notice(sprintf('token已经过期，UID=%d', $result['user_id']) );
            throw new Exception("token过期");
        }
        return $result;
    }

}
